/**
 * 
 */
package org.statewalker.validator;

import java.util.Iterator;

import org.statewalker.dict.BaseDict;
import org.statewalker.dict.IToken;
import org.statewalker.fsm.CompositeFsmStateListener;
import org.statewalker.fsm.FsmProcess;
import org.statewalker.fsm.IFsmStateProvider;
import org.statewalker.fsm.providers.FsmBase;
import org.statewalker.fsm.providers.FsmStateProvider;
import org.statewalker.fsm.providers.IFsmTransitionsRegistry;
import org.statewalker.graph.GraphIterator;

/**
 * @author kotelnikov
 */
public class Validator {

    private class ValidatorStateProvider
        extends
        FsmStateProvider<ValidatorState, IToken> {

        public ValidatorStateProvider() {
            super(fGrammar, BaseDict.ROOT);
        }

        @Override
        protected String getEventKey(IToken token) {
            return token.getKey();
        }

        @Override
        protected String getStateKey(ValidatorState state) {
            return state != null ? state.getKey() : null;
        }

        @Override
        protected ValidatorState newState(
            ValidatorState parent,
            ValidatorState prev,
            IToken entity,
            String stateKey) {
            return Validator.this.newState(parent, entity, stateKey);
        }

    }

    protected ValidatorGrammar fGrammar;

    protected CompositeFsmStateListener<ValidatorState, IToken> fListener = new CompositeFsmStateListener<ValidatorState, IToken>();

    protected FsmProcess<ValidatorState, IToken> fProcess;

    protected IFsmStateProvider<ValidatorState, IToken> fStateProvider;

    protected IToken fToken;

    public Validator(ValidatorGrammar grammar) {
        fGrammar = grammar;
        fStateProvider = new ValidatorStateProvider();
        fProcess = new FsmProcess<ValidatorState, IToken>(
            fStateProvider,
            fListener);
    }

    public void addListeners(IValidatorListener... listeners) {
        for (IValidatorListener listener : listeners) {
            fListener.addListener(listener);
        }
    }

    protected boolean canClose(String stateKey, String tokenKey) {
        return true;
    }

    public void close(IToken token) {
        fToken = token;
        token.setKey(FsmBase.FINAL);
        fProcess.setEvent(token);
        while (fProcess.hasNext()) {
            fProcess.next();
        }
    }

    protected IToken filterToken(IToken token) {
        return token;
    }

    public ValidatorState getCurrentState() {
        ValidatorState state = fProcess.getCurrent();
        return state;
    }

    public String getCurrentStateKey() {
        ValidatorState state = fProcess.getCurrent();
        return state != null ? state.getKey() : null;
    }

    public IFsmTransitionsRegistry getRegistry() {
        return fGrammar;
    }

    protected ValidatorState newState(
        ValidatorState parent,
        IToken token,
        String key) {
        return new ValidatorState(parent, key, token);
    }

    public void next(IToken token) {
        token = filterToken(token);
        if (token == null)
            return;
        fToken = token;
        String tokenKey = token.getKey();
        if (token.isOpen()) {
            onProcess(token, tokenKey, true);
        }
        if (token.isClose()) {
            boolean ok = false;
            for (Iterator<ValidatorState> iterator = fProcess
                .getStateStack(false); iterator.hasNext();) {
                ValidatorState state = iterator.next();
                String stateKey = state.getKey();
                if (stateKey.equals(tokenKey)) {
                    ok = true;
                    break;
                } else if (!canClose(stateKey, tokenKey)) {
                    break;
                }
            }
            if (ok) {
                token.setKey(BaseDict.FINAL);
                onProcess(token, tokenKey, false);
            }
        }
    }

    private void onProcess(IToken entity, String expectedKey, boolean in) {
        fProcess.setEvent(entity);
        int mask = GraphIterator.IN | GraphIterator.LEAF;
        if (!in) {
            mask = GraphIterator.LEAF | GraphIterator.OUT | GraphIterator.NEXT;
        }
        fProcess.setMode(mask);
        while (fProcess.hasNext()) {
            ValidatorState currentState = fProcess.next();
            ValidatorState state = in ? currentState : fProcess.getPrevious();
            if (state != null) {
                String key = state.getKey();
                if (key.equals(expectedKey) || key.equals(BaseDict.ERROR)) {
                    break;
                }
            }
        }
    }

    public void removeListeners(IValidatorListener... listeners) {
        for (IValidatorListener listener : listeners) {
            fListener.removeListener(listener);
        }
    }

}
